﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureFloorUtils.h"
#include "AlgorithmIntersect.h"
#include "AlgorithmPolygonOperate.h"
#include "AlgorithmProject.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "GbmpMergeCollinerCurvesUtils.h"
#include "GbmpStructureProfileSearchUtils.h"
#include "IBody.h"
#include "IElementBasicInformation.h"
#include "IElementParameters.h"
#include "IInstance.h"
#include "ILine2d.h"
#include "ILine3d.h"
#include "IModelLine.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IPlane.h"
#include "IPolyCurve.h"
#include "IStructureSlopeLine.h"
#include "IType.h"
#include "IPositionAssociatedPlane.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace
{
    struct GmLoopTreeNode
    {
        GmLoopTreeNode(const IPolyCurve* loop)
            : m_pLoop(loop)
        {
        };

        // 添加节点
        bool AddNewNode(GmLoopTreeNode* pNewNode, GmLoopTreeNode* pParent);

        bool DeleteChild(GmLoopTreeNode* pChild);

        // 找在自己内部最大的节点
        void FindLargestInTarget(const GmLoopTreeNode* pTarget, std::vector<const IPolyCurve*>& list) const;

        bool In(const GmLoopTreeNode* anther) const;

        // 在自己内部的节点
        std::vector<GmLoopTreeNode*> m_children;

        // 数据节点
        const IPolyCurve* m_pLoop;
    };

    // pParent为空表示是根节点
    bool GmLoopTreeNode::AddNewNode(GmLoopTreeNode* pNewNode, GmLoopTreeNode* pParent)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pNewNode, L"pNewNode不能为空",L"GDMPLab",L"2024-03-30");

        // 根节点改变
        if (nullptr == pParent || pNewNode->In(this))
        {
            FOR_EACH(pChild, this->m_children)
            {
                if (pChild->AddNewNode(pNewNode, this))
                {
                    return true;
                }
            }
            // 相离加在兄弟节点上
            this->m_children.push_back(pNewNode);
            return true;
        }
        else if (this->In(pNewNode))
        {
            bool ok = pParent->DeleteChild(this);
            DBG_WARN_AND_RETURN_FALSE_UNLESS(ok, L"pParent->DeleteChild失败",L"GDMPLab",L"2024-03-30");
            pNewNode->m_children.push_back(this);

            for (auto iter = pParent->m_children.begin(); iter != pParent->m_children.end();)
            {
                if ((*iter)->In(pNewNode))
                {
                    pNewNode->m_children.push_back(*iter);
                    iter = pParent->m_children.erase(iter);
                }
                else
                {
                    iter++;
                }
            }

            pParent->m_children.push_back(pNewNode);

            return true;
        }
        else  // 相离
        {
            return false;
        }
    }

    bool GmLoopTreeNode::DeleteChild(GmLoopTreeNode* pChild)
    {
        auto iter = find(m_children.begin(), m_children.end(), pChild);
        if (iter != m_children.end())
        {
            m_children.erase(iter);
            return true;
        }
        return false;

    }

    void GmLoopTreeNode::FindLargestInTarget(const GmLoopTreeNode* pTarget, std::vector<const IPolyCurve*>& list) const
    {
        if (!pTarget || !pTarget->m_pLoop)
        {
            DBG_WARN(L"pTarget不能为空",L"GDMPLab",L"2024-03-30");
            return;
        }

        // 空节点为树根节点
        if (nullptr == this->m_pLoop || pTarget->In(this))
        {
            FOR_EACH(pChild, m_children)
            {
                pChild->FindLargestInTarget(pTarget, list);
            }
            return;
        }
        else if (this->In(pTarget))
        {
            list.push_back(m_pLoop);
            return;
        }
        else  // 相离
        {
            return;
        }
    }

    bool GmLoopTreeNode::In(const GmLoopTreeNode* anther) const
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(anther, L"anther不能为空",L"GDMPLab",L"2024-03-30");
        return GbmpStructureProfileSearchUtils::IsLoop1InLoop2(m_pLoop, anther->m_pLoop);
    }

    static void TrimeShortCurve(OwnerPtr<IPolyCurve>& opLoop)
    {
        std::vector<OwnerPtr<ICurve2d>> curves;
        bool hasZero = false;
        // 去除短线
        for (int i = 0; i < opLoop->GetCurveCount(); i++)
        {
            OwnerPtr<ICurve2d>  opCurve = opLoop->GetCurve(i);

            if (MathUtils::IsGreaterThan(opCurve->GetLength(), Constants::LENGTH_EPS, Constants::DOUBLE_EPS))
            {
                curves.emplace_back(TransferOwnership(opCurve));
            }
            else
            {
                hasZero = true;
            }
        }

        if (hasZero)
        {
            int size = (int)curves.size();
            // 连接
            for (int i = 0; i < size; i++)
            {
                ICurve2d* pCurCurve = curves[i].get();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pCurCurve, L"pCurCurve为空",L"GDMPLab",L"2024-03-30");
                ICurve2d* pNextCurve = curves[(i + 1) % size].get();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pNextCurve, L"pNextCurve为空",L"GDMPLab",L"2024-03-30");

                Vector2d startPt = pNextCurve->GetStartPoint();
                Vector2d endPt = pCurCurve->GetEndPoint();

                if (!MathUtils::IsZero((startPt - endPt).Length()))
                {
                    // 先不处理其他曲线，有时间再处理
                    if (!dynamic_cast<ILine2d*>(pCurCurve) || !dynamic_cast<ILine2d*>(pNextCurve))
                    {
                        DBG_WARN(L"只支持直线情况",L"GDMPLab",L"2024-03-30");
                        continue;
                    }
                    if (pCurCurve->GetLength() > pNextCurve->GetLength())
                    {
                        startPt = pCurCurve->GetEndPoint();
                        endPt = pNextCurve->GetEndPoint();
                        OwnerPtr<ILine2d> opLine2d = ILine2d::Create(startPt, endPt);
                        curves[(i + 1) % size] = TransferOwnership(opLine2d);
                    }
                    else
                    {
                        startPt = pCurCurve->GetStartPoint();
                        endPt = pNextCurve->GetStartPoint();
                        OwnerPtr<ILine2d> opLine2d = ILine2d::Create(startPt, endPt);
                        curves[i] = TransferOwnership(opLine2d);
                    }
                }
            }
        }
        opLoop = TransferOwnership(IPolyCurve::Create());
        FOR_EACH(opCurve, curves)
        {
            opLoop->AddBackCurve(TransferOwnership(opCurve));
        }
        DBG_WARN_UNLESS(opLoop->IsClosed(), L"opLoop不封闭",L"GDMPLab",L"2024-03-30");
    }

    static bool IsPointOnLine(const Vector2d& point, const ILine2d* pLine, double threshold)
    {
        Vector2d startPt = pLine->GetStartPoint();

        Vector2d norm = pLine->GetStartTangent();
        norm = GbmpStructureProfileSearchUtils::Vector2dRoatate90ClockWise(norm);
        norm.Normalize();
        double d = (startPt - point).Dot(norm);
        return fabs(d) < threshold;
    }

    // 两直线是否离得很近
    static bool IsLinesNear(const ILine2d* pLine1, const ILine2d* pLine2, double threshold)
    {
        bool colinear = (IsPointOnLine(pLine1->GetStartPoint(), pLine2, threshold) && IsPointOnLine(pLine1->GetEndPoint(), pLine2, threshold))
            || (IsPointOnLine(pLine2->GetStartPoint(), pLine1, threshold) && IsPointOnLine(pLine2->GetEndPoint(), pLine1, threshold));
        if (!colinear)
            return false;

        Vector2d ac = pLine1->GetStartPoint() - pLine2->GetStartPoint();
        Vector2d ad = pLine1->GetStartPoint() - pLine2->GetEndPoint();

        Vector2d bc = pLine1->GetEndPoint() - pLine2->GetStartPoint();
        Vector2d bd = pLine1->GetEndPoint() - pLine2->GetEndPoint();

        double lenSum = pLine1->GetLength() + pLine2->GetLength() - threshold;

        return ac.Length() < lenSum
            && ad.Length() < lenSum
            && bc.Length() < lenSum
            && bd.Length() < lenSum;
    }

    // 此处环有效性的概念：环本身有效或者环本身存在问题但能通过搜环得到有效的Polygon
    static bool IsLoopValid(const IPolyCurve* pLoop, std::vector<OwnerPtr<IPolyCurve>> &loopList, bool showDebugInfo)
    {
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pLoop, L"pLoop为空",L"GDMPLab",L"2024-03-30");

        std::vector<OwnerPtr<ICurve2d>> opCurve2dList;
        pLoop->GetAllCurves(opCurve2dList);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(!opCurve2dList.empty(), L"curve2dList.empty()!",L"GDMPLab",L"2024-03-30");


        GbmpMergeCollinerCurvesUtils::Merge(opCurve2dList, Constants::LENGTH_EPS);

        if (opCurve2dList.empty())
        {
            return false;
        }

        std::vector<const ICurve2d *> pCurve2dList;
        FOR_EACH(opCurve2d, opCurve2dList)
        {
            pCurve2dList.push_back(opCurve2d.get());
        }
        std::vector<OwnerPtr<IPolygon>> polygohList;
        AlgorithmPolygonOperate::SearchPolygons(pCurve2dList, Constants::LENGTH_EPS, polygohList);
        if (polygohList.empty() && showDebugInfo)
        {
            std::wstring s = L"搜环失败\n\n";
            FOR_EACH(opCurve2d, opCurve2dList)
            {
                pCurve2dList.push_back(opCurve2d.get());
                s.append(opCurve2d->GetStringForDebug());
                s.append(L"\n\n");
            }
            DBG_WARN(s,L"GDMPLab",L"2024-03-30");
        }
        FOR_EACH(opPolygon, polygohList)
        {
            for (int i = 0; i < opPolygon->GetPolyCurveCount(); i++)
            {
                OwnerPtr<IPolyCurve> opLoop = opPolygon->GetPolyCurve(i);
                if (pLoop->IsAnticlockwise() != opLoop->IsAnticlockwise())
                    opLoop->Reverse();

                loopList.push_back(TransferOwnership(opLoop));
            }
        }

        return !loopList.empty();
    }
 }

bool StructureFloorUtils::CalculateFloorScratchHeight(const IInstance* pInstance, const std::vector<IModelLine*>& profileCurves,const IStructureSlopeLine* pSlopeLine, double & compThickness)
{
    if (pInstance == nullptr)
        return false;

    const IElementBasicInformation* pElementBasicIfmt = pInstance->GetBasicInformation();
    if (pElementBasicIfmt == nullptr)
        return false;

    const IType* pType = pElementBasicIfmt->GetType();
    if (pType == nullptr)
        return false;

    const IElementParameters* pParameters = pType->GetElementParameters();
    if (pType == nullptr)
        return false;

    // 厚度
    double thickness = 0;
    bool hasParameter = pParameters->GetParameterValueAsDouble(PARAMETER_UID(FloorCoreThicknessBuiltInParameter), thickness);
    DBG_WARN_AND_RETURN_UNLESS(hasParameter, 0.0, L"没楼板的厚度参数",L"GDMPLab",L"2024-03-30");

    double offset = 0.0;
    IPositionAssociatedPlane::GetBottomPlaneHeightOffset(pInstance, offset);

    if (pSlopeLine)
    {
        // 定位
        OwnerPtr<ILine3d> opSlopeLine = pSlopeLine->GetSlopeLine();
        if (!opSlopeLine)
            return false;
        Vector3d topCenterPt = opSlopeLine->GetStartPoint();
        Vector3d normal = Vector3d::UnitZ;
        // 坡度
        Vector3d slopAxis = opSlopeLine->GetDirection().Cross(Vector3d::UnitZ);
        normal = slopAxis.Cross(opSlopeLine->GetDirection());
        normal.Normalize();

        Vector3d bottomCenterPt = topCenterPt - normal * thickness;
        // 根据定位、厚度和坡度，计算顶底切平面
        OwnerPtr<IPlane> opTopPlane = IPlane::Create(topCenterPt, normal);
        OwnerPtr<IPlane> opBottomPlane = IPlane::Create(bottomCenterPt, -normal);

        double maxdistance = 0.0;
        FOR_EACH(pModelLine, profileCurves)
        {
            Vector3d  startPt = pModelLine->GetGeometryCurve()->GetStartPoint();
            OwnerPtr<ILine3d> opline = ILine3d::Create(startPt, Vector3d::UnitZ, Intervald(-Constants::MAX_MODEL_SIZE / 2.0, Constants::MAX_MODEL_SIZE / 2.0));

            std::vector<OwnerPtr<ICurve3dSurfaceIntersection>> opIntersectsTop, opIntersectsBottom;
            AlgorithmIntersect::Intersect(opline.get(), opTopPlane.get(), opIntersectsTop, Constants::LENGTH_EPS);
            AlgorithmIntersect::Intersect(opline.get(), opBottomPlane.get(), opIntersectsBottom, Constants::LENGTH_EPS);
            if (opIntersectsTop.size() != 1 || opIntersectsBottom.size() != 1)
            {
                return false;
            }
            Vector3d intersectPtTop = opline->GetPoint(opIntersectsTop.front()->GetParamOnCurve());
            Vector3d intersectPtBottom = opline->GetPoint(opIntersectsBottom.front()->GetParamOnCurve());

            double toTopDist = (intersectPtTop - startPt).Length();
            double toBottomDist = (intersectPtBottom - startPt).Length();

            double maxDis = toTopDist > toBottomDist ? toTopDist : toBottomDist;
            DBG_WARN_AND_RETURN_UNLESS(maxDis > 0, 0.0, L"楼板的数据计算错误！",L"GDMPLab",L"2024-03-30");
            if (maxDis > maxdistance)
            {
                maxdistance = maxDis;
            }
        }
        compThickness = maxdistance + 2 * thickness + 2 * offset;
    }
    else
    {
        compThickness = 2 * thickness + 2 * offset;
    }
    return true;
}

bool StructureFloorUtils::GetFloorProfilePolygonAutoRecognizeAlgorithm(const std::vector<const IBody*>& bodyList, const Coordinate3d& coord, bool showDebugInfo, std::vector<OwnerPtr<IPolygon>>& polygonList)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(!bodyList.empty(), L"bodyList为空",L"GDMPLab",L"2024-03-30");
    if (showDebugInfo)
    {
        std::wstring str = L"";
        FOR_EACH(pBody, bodyList)
        {
            str.append(pBody->GetStringForDebug());
            str.append(L"\n");
        }
        DBG_WARN(str,L"GDMPLab",L"2024-03-30");
    }

    // 1.投影
    OwnerPtr<IPolygon> opPolygonForSearch = GbmpStructureProfileSearchUtils::ProjectBodiesAndBoolUnite(bodyList, coord);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(opPolygonForSearch.get(), L"opPolygon不能为空",L"GDMPLab",L"2024-03-30");

    if (showDebugInfo)
    {
        DBG_WARN(opPolygonForSearch->GetStringForDebug(),L"GDMPLab",L"2024-03-30");
    }

    // OwnerPtr存放处，避免析构
    std::vector<OwnerPtr<IPolyCurve>> outterLoopCollect;
    std::vector<OwnerPtr<GmLoopTreeNode>> nodeCollect;

    // 建树,根节点数据为空
    nodeCollect.emplace_back(NEW_AS_OWNER_PTR(GmLoopTreeNode, nullptr));
    GmLoopTreeNode* const pRoot = nodeCollect[0].get();

    // 楼板loop，内环
    std::vector<OwnerPtr<IPolyCurve>> floorLoops;

    // 2.内外环分组
    for (int i = 0; i < opPolygonForSearch->GetPolyCurveCount(); i++)
    {
        OwnerPtr<IPolyCurve> opCopiedLoop = opPolygonForSearch->GetPolyCurve(i);
        TrimeShortCurve(opCopiedLoop);

        // 处理距离很近的loop
        std::vector<OwnerPtr<IPolyCurve>> tmpLoopList;
        if (!IsLoopValid(opCopiedLoop.get(), tmpLoopList, showDebugInfo))
            continue;

        FOR_EACH(opLoop, tmpLoopList)
        {
            if (opLoop->IsAnticlockwise())
            {
                nodeCollect.emplace_back(NEW_AS_OWNER_PTR(GmLoopTreeNode, opLoop.get()));
                pRoot->AddNewNode(nodeCollect.back().get(), nullptr);
                outterLoopCollect.emplace_back(TransferOwnership(opLoop));
            }
            else
            {
                opLoop->Reverse();
                floorLoops.emplace_back(TransferOwnership(opLoop));
            }
        }

    }
    if (floorLoops.empty())
        return false;

    polygonList.reserve(floorLoops.size());

    // 3.组合楼板轮廓和板洞
    FOR_EACH(opFloorLoop, floorLoops)
    {
        polygonList.emplace_back(IPolygon::Create());
        IPolygon* pPolygon = polygonList.back().get();
        pPolygon->AddPolyCurveByClone(opFloorLoop.get());

        GmLoopTreeNode curNode(opFloorLoop.get());

        std::vector<const IPolyCurve*> list;
        pRoot->FindLargestInTarget(&curNode, list);
        FOR_EACH(pLoop, list)
        {
            OwnerPtr<IPolyCurve> opNewLoop = TransferOwnershipCast<IPolyCurve>(pLoop->Clone());
            opNewLoop->Reverse();
            pPolygon->AddPolyCurveByClone(opNewLoop.get());
        }

        DBG_WARN_AND_RETURN_FALSE_UNLESS(pPolygon->IsRegularized(), L"polygon不合理",L"GDMPLab",L"2024-03-30");
    }

    if (showDebugInfo)
    {
        std::wstring str = L"识别出的轮廓\n\n";
        FOR_EACH(opPolygon, polygonList)
        {
            str.append(opPolygon->GetStringForDebug());
            str.append(L"\n\n");
        }
        DBG_WARN(str,L"GDMPLab",L"2024-03-30");
    }

    return true;
}
